function [result_image] = dithering_floyd_steinberg(original_image, result_bits)
% DITHERING

% Algoritmo di Floyd-Steinberg
% - Distribuisce ai pixel adiacenti informazioni sull'errore di
%   quantizzazione del pixel corrente

% Profondita' in bit dell'immagine originale
original_bits = 8;
original_levels = 2^original_bits-1;

% Setto il numero di bit dell'immagine in uscita (< di quello in entrata)
result_levels = 2^result_bits-1;

% Creo il frame dell'immagine risultante (con zero padding per l'algoritmo)
result_image = zeros(size(original_image, 1)+2,...
    size(original_image, 2)+2,...
    size(original_image, 3));

% Maschera dell'algoritmo per l'ERRORE DI QUANTIZZAZIONE
% i quali parametri sono stati scelti in base a studi sull'occhio umano
dither_mask = (1/16) * [0 0 7; 3 5 1];

% ALGORITMO
% per ogni canale colore
for c = 1:size(original_image, 3)

    % per ogni y (da sopra a sotto) senza contare il padding
    for y = 2:size(result_image, 1)-1

        % per ogni x (da sinistra a destra) senza contare il padding
        for x = 2:size(result_image, 2)-1

            % ricavo il pixel originale + l'errore di quantizzazione dei
            % pixel vicini (calcolati le iterazioni prima)
            old_pixel = double(original_image(y-1,x-1,c)) +...
                result_image(y,x,c);

            % quantizzo il pixel e calcolo l'errore di quantizzazione
            new_pixel = ...
                round(old_pixel * (result_levels/original_levels))*...
                (original_levels/result_levels);
            error = old_pixel - new_pixel;

            % imposto la maschera con l'errore come parametro: rappresenta
            % la distribuzione dell'errore di quantizzazione del pixel
            % corrente che verra' usato dai pixel adiacenti
            current_mask = dither_mask .* error;

            % applico la maschera all'immagine, centrandola sul
            % pixel corrente
            result_image(y:y+1,x-1:x+1,c) =...
                result_image(y:y+1,x-1:x+1,c) + current_mask;

            % salvo il risultato vero e proprio nel pixel corrente
            % NB: questo risultato non verra' piu' toccato dall'algoritmo
            result_image(y,x,c) = new_pixel;
        end
    end
end

end